#include "DiskOperations.c"
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>

#define  SECSIZEINT  128
#define  INODENUMBER  128
#define  MAXROOTSIZE  32

/*
*
 * @authors: Nick Phillips and to some extend Conrad Stack (naphill2 and jcstack, respectively)
 * 
 * address any bugs: jcstack@ncsu.edu
 * 
 * YAM.c implement the YAM filesystem complete with everything
 * that you see when you type the 'help' command at the YAM
 * system prompt.  Allows for most basic filesystem functions.
 * 
 * @param Inode is used for the inodes
 * @param DirEntry is a directory entry
 * @param DirSector creates a directory sector
 * @param InodeBitmap is the bitmap for the inodes
 * @param SectorBitmap is the bitmap for the sectors
 * @param node is the inode table
 * @param root is the root directory
 * 
 */
  




struct Inode{
   unsigned short int type;
   unsigned short int size;
   unsigned short int sect[4];
   unsigned short int indirect;
   unsigned short int linkCount;
};

struct DirEntry{
   char filename[14];
   unsigned short int index;
};

struct DirSector{
   struct DirEntry entry[MAXROOTSIZE];
};

/* global variables */
unsigned int InodeBitmap[SECSIZEINT];
unsigned int SectorBitmap[SECSIZEINT*4];
struct Inode node[INODENUMBER];
struct DirSector root;


/* function list */
void synch();
void format();
void copy(char file[],char YAMfile[]);
int getNode();
void freeNode();
int getMem();
void freeMem();
char* add(char name[], int index);
void link(char newname[], char YAMfile[]);
void rm(char YAMfile[]);
void display(int numOfBytes, int sec);
void ls(char dirname[]);
void cat(char YAMfile[]);
void export(char Unixfile[]);
void import(char Unixfile[]);
void help();


int main(){
    char *command;
    char *arg1;
    char *arg2;
    char line[50];
    int var1;
    int var2;
    
    format();
    while(1){

        /* displays command prompt, get user input */
        printf(">>");
        fgets(line,sizeof(line),stdin);
        command = strtok(line," ");
        

        /* handles copy command */
        if(strcmp(command, "cp")==0){
            arg1 = strtok(NULL," ");
            arg2 = strtok(NULL," ");
            if(!arg1 | !arg2){
                printf("usage: copy <UNIXfile> <YAMfile>\n");
            }
            else{
                arg2[strlen(arg2)-1] = '\0';
                copy(arg1, arg2);
            }
        }

        /* handles rm command */
        else if(strcmp(command, "rm")==0){
            arg1 = strtok(NULL," ");
            if(!arg1)
                printf("usage: rm <YAMfile>\n");
            else{
                arg1[strlen(arg1)-1] = '\0';
                rm(arg1);
            }
        }

        /* handles export command */
        else if(strcmp(command, "export")==0){
            arg1 = strtok(NULL," ");
            if(!arg1)
                printf("usage: rm <YAMfile>\n");
            else{
                arg1[strlen(arg1)-1] = '\0';
                export(arg1);
            }
        }
        
        /* handles import command */
        else if(strcmp(command, "import")==0){
            arg1 = strtok(NULL," ");
            if(!arg1)
                printf("usage: rm <YAMfile>\n");
            else{
                arg1[strlen(arg1)-1] = '\0';
                import(arg1);
            }
        }
        

        /* handles ls command */
        else if(strcmp(command, "ls")==0){
            arg1 = strtok(NULL," ");
            if(!arg1)
                printf("usage: ls <directory name>\n");
            else{
               ls(arg1);
            }

        }

        /* handles display command */
        else if(strcmp(command, "display")==0){
            arg1 = strtok(NULL," ");
            arg2 = strtok(NULL," ");
            if(!arg1 | !arg2)
                printf("usage: display <numberofbytes> <sectornumber>\n");
            else{
                var1 = atoi(arg1);
                var2 = atoi(arg2);
                display(var1,var2);
            }
        }

        /* handles link commmand */
        else if(strcmp(command, "ln")==0){
            arg1 = strtok(NULL," ");
            arg2 = strtok(NULL," ");
            if(!arg1 | !arg2)
                printf("usage: ln <sourcefile> <targetfile>\n");
            else{
                arg2[strlen(arg2)-1] = '\0';
                link(arg1,arg2);
            }
        }

        else if(strcmp(command, "cat")==0){
            arg1 = strtok(NULL," ");
            if(!arg1 | (strcmp(arg1,"")==0))
                printf("usage: cat <YAMfile>\n");
            else{
                arg1[strlen(arg1)-1] = '\0';
                cat(arg1);
            }
        }

        else if(strcmp(command,"format\n")==0){
            format();
        }

        /* handles synch commmand */
        else if(strcmp(command, "synch\n")==0){
            synch();
        }

        else if(strcmp(command, "help\n")==0){
            help();
        }
        /* handles logout command */
        else if(strcmp(command, "logout\n")==0){
            return EXIT_SUCCESS;
        }

        /* handles everything else */
        else{
            printf("invalid command, type 'help' to see options\n");
        }
    }
        

    return EXIT_SUCCESS;
}

/*
* format method formats the YAM disk drive to its initial
 * settings.
 * @param n counter for for-loop
 */ 
void format() {
        int n;

        InodeBitmap[0] = 0x80000000;
        for (n=1; n < SECSIZEINT; n++)
           InodeBitmap[n] = 0;
        
        SectorBitmap[0] = 0xFFC00000;
        for (n=1; n < SECSIZEINT*4; n++)
           SectorBitmap[n] = 0;
        
        node[0].type = 0;
        node[0].size = SIZEOFSECTOR;
        node[0].sect[0] = 9;
        node[0].linkCount = 1;
        
        for (n=0;n<INODENUMBER;n++) {
            node[n].indirect = 0;
        }

        for (n=0; n < MAXROOTSIZE; n++)
           root.entry[n].index = 0;
        
        synch();
}

/*
*
 * display method will display a specified number of bytes
 * from a specified sector
 * @param k counter for loops
 * @param data hold the data to be displayed
 */ 

void display(int byteCount, int secNumber) {
	int k;
	unsigned char data[SIZEOFSECTOR];

	if (byteCount < 1  ||  byteCount > SIZEOFSECTOR) {
	   printf("Invalid number of bytes\n");
	   return;
	}

	if (secNumber < 0  ||  secNumber >= SIZEOFDISK) {
	   printf("Invalid sector number\n");
	   return;
	}

	printf("Sector %d:\n\n", secNumber);
	readSector(secNumber, (struct Sector*)data);
	for (k=0; k < byteCount; k++)
	    printf("%0.2x", data[k]);
	printf("\n\n");
}


/* 
* synch method moves contents of local buffer onto the 
 * YAM disk
 * @param n is a counter for the loops
 * @param x holds return from writeSector
 */
void synch() {
        int n,x;
        
        x = writeSector(0, (struct Sector*)InodeBitmap);

        for (n=0; n <= 3; n++)
           x = writeSector(n+1, (struct Sector*)(&SectorBitmap[n*SECSIZEINT]));

        for (n=0; n <= 3; n++)
           x = writeSector(n+5, (struct Sector*)(&node[n*(INODENUMBER/4)]));

        x = writeSector(9, (struct Sector*)(&root));
}


/*
*
 * copy method copys a file from unix to the YAM file system
 * @param s holds mem location
 * @param i is a counter for the loops
 * @param fd holds the file pointer
 * @param fbytes holds the number of bytes in the file
 * @param k is a loop counter
 * @param iSec is the indirect sector location if needed
 * @param message is the message to the user about the results
 * @param buffer a buffer for the data coming in
 * @param indSector holds the list of indirect sectors
 */ 
void copy(char file[], char YAMfile[]) {
        int s, i, fd, fbytes, k, iSec;
        char* message;
        char buffer[SIZEOFSECTOR];
        unsigned short int indSector[SECSIZEINT*2];

        fd = open(file, O_RDONLY);
        fbytes = read(fd, buffer, SIZEOFSECTOR);
        if (fbytes < 0) {
           printf("Error reading file %s\n", file);
           return;
        }

        s = getMem();
        if (s < 0) {
           printf("Disk is full!\nFile was not copied.\n");
           close(fd);
           return;
        }

        i = getNode();
        message = add(YAMfile, i);
        if (strcmp(message, "") != 0) {
           freeNode(i);
           freeMem(s);
           printf("%s\n", message);
           close(fd);
           return;
        }

        node[i].type = 1;
        node[i].sect[0] = s;
        node[i].linkCount = 1;
        writeSector(s, (struct Sector *)buffer);
        node[i].size = fbytes;

        for (k=1; k < 4; k++) {
           fbytes = read(fd, buffer, SIZEOFSECTOR);
           s = getMem();
           if (fbytes <= 0  ||  s < 0) {
                printf("%d bytes copied.\n", node[i].size);
                freeMem(s);
                close(fd);
                return;
           }
           writeSector(s, (struct Sector *)buffer);
           node[i].size += fbytes;
           node[i].sect[k] = s;
        }
        fbytes = read(fd, buffer, SIZEOFSECTOR);
        iSec = getMem();
        s = getMem();
        if (fbytes <= 0  ||  s < 0) {
                printf("%d bytes copied.\n", node[i].size);
                freeMem(iSec);
                freeMem(s);
                close(fd);
                return;
        }
        writeSector(s, (struct Sector *)buffer);
        node[i].size += fbytes;
        indSector[0] = s;
        node[i].indirect = iSec;
        for (k=1; k < 124; k++) {
           fbytes = read(fd, buffer, SIZEOFSECTOR);
           s = getMem();
           if (fbytes <= 0  ||  s < 0) {
                printf("%d bytes copied.\n", node[i].size);
                freeMem(s);
                writeSector(iSec, (struct Sector *)indSector);
                close(fd);
                return;
           }
           writeSector(s, (struct Sector *)buffer);
           node[i].size += fbytes;
           indSector[k] = s;
        }
        printf("%d bytes copied.\n", node[i].size);
        writeSector(iSec, (struct Sector *)indSector);
        close(fd);
}


/**
 *Finds the first free inode (i.e. 0 value) and sets it to one.
 *@return Position of free inode.  -1 if no inodes are free.
 */
int getNode() {
   unsigned int check;
   int n;
   int pos;
   
   for (n=0; n < SECSIZEINT; n++) {
     check = 0x80000000;
     for (pos=0; pos < 32; pos++) {
        if ((InodeBitmap[n] & check) == 0) {
           InodeBitmap[n] = (InodeBitmap[n] | check);
           return (n*32)+pos;
        }
        check = check >> 1;
     }
   }
   
   return  -1;
}

/**
 *Sets the inode bit at position to 0.
 *@param position The inode bit to set
 */
void freeNode(int position) {
   int location;
   unsigned int mask = 0x80000000;
   
   if (position<1 || position>SECSIZEINT)
      return;

   location = position/32;
   position = position % 32;
   mask = mask >> position;
   mask = ~mask;
   
   InodeBitmap[location] = InodeBitmap[location] & mask;
}

/**
 *Finds the first free sector (i.e. 0 value) and sets it to one.
 *@return Position of free sector.  -1 if no sectors are free.
 */
int getMem() {
   unsigned int check;
   int n;
   int pos;
   
   for (n=0; n < SECSIZEINT*4; n++) {
     check = 0x80000000;
     for (pos=0; pos < 32; pos++) {
        if ((SectorBitmap[n] & check) == 0) {
           SectorBitmap[n] = (SectorBitmap[n] | check);
           return (n*32)+pos;
        }
        check = check >> 1;
     }
   }
   
   return  -1;
}

/**
 *Sets the bit at position to 0.
 *@param position The bit to set
 */
void freeMem(int position) {
   int location;
   unsigned int mask = 0x80000000;
   
   if (position<10 || position>SECSIZEINT*4)
      return;

   location = position/32;
   position = position % 32;
   mask = mask >> position;
   mask = ~mask;
   
   SectorBitmap[location] = SectorBitmap[location] & mask;
}

/**
 *Adds the new file to the directory if there is room and it's not a duplicate.
 *@param name The filename
 *@param index The inode number
 *@return Message string
 */
char* add(char name[], int index) {
   int x;
   int length;
   int empty = -1;
   
   length = strlen(name);
   if (length > 13)
     return "Filename is too long";
     
   if (index <= 0)
     return "Invalid inode";
   
   printf("filename is: %s\n",name);
   for (x=0; x < length; x++) {
      if (name[x]>='A'  &&  name[x]<='Z')
         continue;
      if (name[x]>='a'  &&  name[x]<='z')
         continue;
      if (name[x]>='0'  &&  name[x]<='9')
         continue;
      if (name[x] == '.')
         continue;
      if (name[x] == '_')
         continue;
      if (name[x] == ',')
         continue;

      return "Invalid filename";
   }
   
   for (x=0; x < MAXROOTSIZE; x++) {
      if (root.entry[x].index > 0) {
          if (strcmp(root.entry[x].filename, name)==0)
            return "That filename already exists";
      }
      else
          empty = x;
   }
   
   if (empty < 0)
      return "The directory is full";
   
   strcpy(root.entry[empty].filename, name);
   root.entry[empty].index = index;
   return "";
}

/*
*
 * link method makes another hard link to an existing file
 * @param k is a loop counter
 * @param i holds the link count
 * @param message holds a message on the results for the user
 */ 
void link(char newname[], char YAMfile[]) {
        int k;
        unsigned short int i = 0;
        char* message;

        for (k=0; k < MAXROOTSIZE; k++) {
           if (root.entry[k].index > 0) {
                if (strcmp(root.entry[k].filename, YAMfile) == 0)
                   i = root.entry[k].index;
           }
        }

        if (i == 0) {
           printf("Could not find %s\n", YAMfile);
           return;
        }

        message = add(newname, i);
        if (strcmp(message, "") != 0) {
           printf("%s\n", message);
           return;
        }

        node[i].linkCount++;
}

/*
*
 * rm method removes a file from the YAM filesystem
 * @param k is a loop counter
 * @param filesize is the size of the file to remove
 * @param i is the node at which the file resides
 * @param indSector holds the list of more sectors to free
 */ 

void rm(char YAMfile[]) {
        int k, filesize, count;
        unsigned short int i = 0;
        unsigned short int indSector[SECSIZEINT*2];

        for (k=0; k < MAXROOTSIZE; k++) {
           if (root.entry[k].index > 0) {
                if (strcmp(root.entry[k].filename, YAMfile) == 0) {
                   i = root.entry[k].index;
                   root.entry[k].index = 0;
                }
           }
        }

        if (i == 0) {
           printf("Could not find %s\n", YAMfile);
           return;
        }

        if (node[i].linkCount > 1) {
           node[i].linkCount--;
           return;
        }

        filesize = node[i].size / SIZEOFSECTOR;
        if ((node[i].size % SIZEOFSECTOR) > 0)
            filesize++;

        if (filesize > 4)
           count = 4;
        else
           count = filesize;

        for (k=0; k < count; k++)
           freeMem(node[i].sect[k]);

        if (filesize > 4) {
                readSector(node[i].indirect, (struct Sector*)indSector);
                for (k=0; k < filesize-4; k++)
                   freeMem(indSector[k]);
                freeMem(node[i].indirect);
        }

        freeNode(i);
}


/*
*
 * ls method prints out all the files in YAM
 * @param i,j,k are loop counters
 * @param idr is the indirect sector if it has one
 * @param size is the size of the file
 * @param temp is the number of indirect sectors are used
 * @param indSector is the list of the indirect sectors
 */ 
void ls(char dirname[]){
    int i,j,k,idr;
    unsigned short int size,temp;
    unsigned short int indSector[SECSIZEINT*2];


    readSector(9,(struct Sector *)&root);
    printf("File\tSize(in bytes)\tSectors\n");
    printf("-----------------------------------------------\n");
    for(i=0;i<MAXROOTSIZE;i++){
        if(root.entry[i].index != 0){
            size = node[root.entry[i].index].size;
            size = (size - (4*512));
            temp = size%512;
            size = size/512;
            printf("%s\t%d\t\t",root.entry[i].filename, node[root.entry[i].index].size);
            for(j = 0;j<4;j++){
                if(node[root.entry[i].index].sect[j] != 0)
                    printf("%d ",node[root.entry[i].index].sect[j]);
            }
            if((idr = node[root.entry[i].index].indirect) != 0){
                
                readSector(idr, (struct Sector*)indSector);
                if(temp != 0)
                    size++;
                for(k=0;k<size;k++){
                    printf("%hu ",indSector[k]);
                }
            }
            printf("\n");
        }
    }
}


/*
*
 * cat method prints out a YAM file
 * @param i,j,k are loop counters
 * @param size is the size of the file
 * @param inode is the inode of the file
 * @param p is the offset into the last sector in which the file resides
 * @param temp, holds the last sector
 * @param indSector holds the list of indirect sectors
 */ 
void cat(char YAMfile[]){
    int i,j,k;
    unsigned short int size,inode,p;
    char temp[512];
    unsigned short int indSector[SECSIZEINT*2];
    
    
    readSector(9,(struct Sector *)&root);
    
    for(i=0;i<MAXROOTSIZE;i++){
        if(strcmp(root.entry[i].filename, YAMfile)==0){
            j = i;
        }
    }
    if(!j){
        printf("filename not found\n");
        return;
    }
    printf("found file: %s at %d", root.entry[j].filename, root.entry[j].index);
    inode = root.entry[j].index;
    size = node[inode].size;
    k = size / 512;
    p = size%512;
    if(k == 0){
        readSector(node[inode].sect[0],(struct Sector*)temp);
        temp[p] = '\0';
        printf("%s",temp);
        return;
    }
    if((size%512) != 0)
        k++;
    
    if(node[inode].indirect != 0)
        readSector(node[inode].indirect, (struct Sector*)indSector); 
    for(i=0;i<k;i++){
        if(i < 4){
            readSector(node[inode].sect[i], (struct Sector *)temp);
            printf("%s",temp);
        }
        if(i > 3){
            readSector(indSector[i-4], (struct Sector *)temp);
            if((i+1) == k) 
               temp[p] = '\0';
            printf("%s",temp);
        }

    }
    printf("\n\n");
      
}

/*
*
 * export method exports YAM to a Unix file
 * @param k loop counter
 * @param fd pointer to a file
 * @param n holds return from write
 * @param buffer holds data to be transfer to unix file
 */ 
void export(char Unixfile[]) {
	int k, fd, n;
	struct Sector buffer;

	fd = open(Unixfile, O_WRONLY |O_CREAT | O_TRUNC, 0777);
	n = write(fd, InodeBitmap, sizeof(InodeBitmap));
	n = write(fd, SectorBitmap, sizeof(SectorBitmap));
	n = write(fd, node, sizeof(node));
	n = write(fd, &root, sizeof(root));

	for (k=10; k < SIZEOFDISK; k++) {
	   readSector(k, &buffer);
	   n = write(fd, &buffer, SIZEOFSECTOR);
	}
	close(fd);
}

/*
*
 * import method imports a unix file that was hopefully exported at some
 * point through YAM.
 * @param k loop counter
 * @param fd is the file pointer
 * @param n read pointer
 * @param bufffer holds data to come into YAM
 */ 
void import(char Unixfile[]) {
	int k, fd, n;
	struct Sector buffer;

	fd = open(Unixfile, O_RDONLY);
	if (fd < 0) {
	   printf("Error opening file!\n");
	   return;
	}

	n = read(fd, InodeBitmap, sizeof(InodeBitmap));
	n = read(fd, SectorBitmap, sizeof(SectorBitmap));
	n = read(fd, node, sizeof(node));
	n = read(fd, &root, sizeof(root));

	for (k=10; k < SIZEOFDISK; k++) {
	   n = read(fd, &buffer, SIZEOFSECTOR);
	   if (n < 0) {
		printf("Wrong file format!\nYAM file system may be corrupted.\n");
		return;
	   }
	   writeSector(k, &buffer);
	}
	close(fd);
}

/*
*
 * help method, ahh yes the help method
 */ 
void help(){
    
    printf("COMMAND		help\n\n");

    printf("DESCRIPTION	help\n\n");

    printf("EXAMPLE		>> help\n\n");

    printf("\tCurrent commands are\n\n");
	
    printf("\thelp	displays this information\n\n");
	
    printf("\tformat	formats (initializes) the RAM disk according to the\n");
    printf("\t\tYAM  specifications\n\n");

    printf("\tls     displays the YAM directory\n\n");

    printf("\tcp     copy a file from Unix to  YAM\n\n");

    printf("\tcat    displays the contents of a file to standard output\n\n");

    printf("\tln     creates a hard link\n\n");

    printf("\trm       removes a file from YAM \n\n");

    printf("\tmkdir    makes a directory\n\n");

    printf("\tsynch    writes all YAM file system data/tables cached in\n");
    printf("\t\tprogram to the disk image\n\n");

    printf("\tdisplay displays a specific  number of bytes from\n");
    printf("\t\tadd specific sector in hexadecimal format\n\n");

    printf("\texport    save the contents of the entire RAM disk\n");
    printf("\t\timage into a Unix file\n\n");

    printf("\timport    restore the YAM disk image from a Unix file\n\n");

    printf("\tlogout	   exit the shell program\n\n");
}



